const express = require("express");
const http = require("http");//起服务
const path = require("path");//Node 自带模块 - 拆分目录
const logger = require('morgan');//处理日志
var session = require('express-session');//处理session
var cookieParser = require('cookie-parser');
var createError = require('http-errors');
const cors = require("cors");//后端解决跨域问题
var debug = require('debug')('db:server');

//执行app.js时连接数据库
const connect = require("./utils/connect.js");


// 设置路由
//服务器路由 - routes中的路由(单个js;下面中间件设置的别名是路由的上一级目录)  注意: 不用写才分目录; 在app.js里面是app.get/在router里面是router.get
// 地址栏: app.js中的别名 + 路由的path
var blogRouter = require('./routes/blog');

const app = express();//app===express

// 解决跨域
app.use(cors());//裸奔,任何人都可以访问

// session中间件: 是有跨域的,前端放在nginx是需要配置代理解决,不能用基路径(虽然后台是允许跨域的)
app.use(session({
  name: "my-server",
  secret: "mima blog",
  resave: false,
  saveUninitialized: true,
  cookie: { maxAge: 1000 * 60 * 60 },  // session 保存时长  1hour 
}))


/************* 其他中间件 **************/
app.set('trust proxy', 1) // trust first proxy
// .ejs(html预编译)
app.set('view engine', 'ejs');//设置模板引擎
app.use(logger('dev'));//使用(没有就设置)日志中间件

//设置中间件:将views "拆分" 到根目录(__dirname) -- 访问时跳过views
// views中的.ejs 是 利用.ejs实现js解析的html内容
app.set('views', path.join(__dirname, 'views'));
// 前面也可以加一个参数'/static',表示访问的是静态目录 -- 前缀地址(但没必要)
// 如果要使用多个静态资源目录，请多次调用 express.static 中间件函数
// 对外开放访问(不需要像views写路由才能访问)且"拆分" public(static静态目录) -- 不用写路由/ssr,"直接访问静态文件" -- 目的
// public  static   --  当做 服务器端的静态文件(js,img,css等等)
// 访问静态资源文件时，express.static 中间件函数会根据目录的添加顺序查找所需的文件。
app.use(express.static(path.join(__dirname, 'public')));
// app.use(express.static('public'));//简写

// // 使用req.body可以获取post请求的内容
// // 若没有这两句话: 则不能使用req.body获取
// // post 浏览器不能解析,打开会跳转404; 一般用测试工具postman
app.use(express.json());//获取 POST 请求的ForData数据 => 相当于$.post
app.use(express.urlencoded({ extended: false }));//表单Form action+name+value

// cookie
app.use(cookieParser());

// 测试
app.get("/ceshi", (req, res) => {
  res.json({
    code: 200,
    msg: req.query
  })
});

// 检验token  (用了token,判断token匹配后才能进入/vue)-所以放在session后面,/vue的前面
// const { checkToken } = require("./utils/index");
// app.use(checkToken);

/**** 路由中间件 必须写在别的中间件后面,顺序执行的 ****/
app.use('/apiblog', blogRouter);



/******************** error **********************/
// 需配置path拆分error.ejs的views目录;安装ejs解析error.ejs
// 原始错误:Cannot GET /vue/moviea;写了这个监听错误后更详细
// catch 404 and forward to error handler
// app.use(function (req, res, next) {
//     // next 进入执行下一个中间件;必须写(不然没执行下一个,没写的省略了)
//     next(createError(404));
// });

// error handler  //参数err接上一个中间件的next中的404
// app.use(function (err, req, res, next) {
//     // set locals, only providing error in development
//     res.locals.message = err.message;
//     res.locals.error = req.app.get('env') === 'development' ? err : {};
//     // render the error page
//     // 状态码 500  服务器端报错
//     res.status(err.status || 500);
//     // render 渲染(SSR 服务器端)
//     // 是views中的error.ejs(但是被拆分;不需要写views)
//     // ejs 本质是一个js文件
//     res.render('error');// error会将你的详细错误报出来
// });
// 一个文件导出一个接口: 这里不用导出到www


// catch 404 and forward to error handler
app.use(function (req, res, next) {
  next(createError(404)); app.set('trust proxy', 1) // trust first proxy
  app.use(session({
    secret: 'keyboard cat',
    resave: false,
    saveUninitialized: true,
    cookie: { secure: true }
  }))
});

// error handler
app.use(function (err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};

  // render the error page
  res.status(err.status || 500);
  res.render('error');
});

/************* 起服务start 不用写在最后面  express在www里面 **************/
// 进程.开发环境.端口 || 3000
// www文件比起服务多normalizePort(处理端口),onError(错误回调),onListening(listening回调).
/**
 * Get port from environment and store in Express.
 */
const port = normalizePort(process.env.PORT || '3006');
const hostname = "0.0.0.0";

/**
 * Create HTTP server.
 */
const server = http.createServer(app);

/**
 * Listen on provided port, on all network interfaces.
 */
server.listen(port, hostname, () => {
  console.log(`My express api server start successful at http://${hostname}:${port}`);
})
server.on('error', onError);
server.on('listening', onListening);

/**
 * www
 */
/**
 * Normalize a port into a number, string, or false.
 */
function normalizePort(val) {
  var port = parseInt(val, 10);

  if (isNaN(port)) {
    // named pipe
    return val;
  }

  if (port >= 0) {
    // port number
    return port;
  }

  return false;
}

/**
 * Event listener for HTTP server "error" event.
 */

function onError(error) {
  if (error.syscall !== 'listen') {
    throw error;
  }

  var bind = typeof port === 'string'
    ? 'Pipe ' + port
    : 'Port ' + port;

  // handle specific listen errors with friendly messages
  switch (error.code) {
    case 'EACCES':
      console.error(bind + ' requires elevated privileges');
      process.exit(1);
      break;
    case 'EADDRINUSE':
      console.error(bind + ' is already in use');
      process.exit(1);
      break;
    default:
      throw error;
  }
}

/**
 * Event listener for HTTP server "listening" event.
 */

function onListening() {
  var addr = server.address();
  var bind = typeof addr === 'string'
    ? 'pipe ' + addr
    : 'port ' + addr.port;
  debug('Listening on ' + bind);
}

/************* 起服务结束 不用写在最后面  express在www里面 **************/

module.exports = app;
